今天除了要修正上篇的 GenAI 幻覺外,也會進行實作上常需要調整圖視的擺放及順序,GenAI 可以很快幫上忙,只要你清楚圖示上的資料是在 XML 的哪一段。
接著會發現昨天的程式是有問題的,這也是大名鼎鼎 GenAI 的幻覺案例,但只要能確定問題出在哪 (錯誤訊息如下),GenAI 就能很容易解決,此時也別忘了要指定版本。
昨天的錯誤訊息,我們大致判斷出是最後的 Search View 有問題
Failed to load registry
Traceback (most recent call last):
File "/home/odoo/src/odoo/odoo/modules/registry.py", line 114, in new
odoo.modules.load_modules(registry, force_demo, status, update_module)
....
File "/home/odoo/src/odoo/odoo/tools/convert.py", line 567, in _tag_root
raise ParseError(msg) from None # Restart with "--log-handler odoo.tools.convert:DEBUG" for complete traceback
odoo.tools.convert.ParseError: while parsing /home/odoo/src/user/library/views/book_views.xml:48
Invalid view library.books.search definition in library/views/book_views.xml
View error context:
'-no context-'
"""
#books.py
from odoo import models, fields, api
from odoo.exceptions import ValidationError
class Books(models.Model):
_name = 'library.books'
_description = 'Library Books'
name = fields.Char(string='Book Name', required=True)
isbn = fields.Char(string='ISBN')
book_type = fields.Selection([
('paper', 'Paper'),
('ebook', 'E-Book'),
('other', 'Other')],
string='Book Type', default='paper')
description = fields.Text(string='Description')
stock = fields.Integer(string='Stock')
price_twd = fields.Float(string='Price (TWD)')
average_rating = fields.Float(string='Average Rating', digits=(2, 2))
publish_date = fields.Date(string='Publish Date')
last_rent_date = fields.Datetime(string='Last Rent Date')
rentable = fields.Boolean(string='Rentable', default=True)
cover_image = fields.Binary(string='Cover Image')
@api.model
def is_valid_isbn(self, isbn):
"""簡單的 ISBN 驗證方法,支援 ISBN-10 和 ISBN-13"""
isbn = isbn.replace('-', '').replace(' ', '')
if len(isbn) == 10:
total = sum((10 - i) * int(x) for i, x in enumerate(isbn[:9])) + (10 if isbn[-1] == 'X' else int(isbn[-1]))
return total % 11 == 0
elif len(isbn) == 13:
total = sum(int(x) * (3 if i % 2 else 1) for i, x in enumerate(isbn))
return total % 10 == 0
return False
def check_isbn(self):
"""檢查 ISBN,並在無效時拋出錯誤"""
if not self.isbn:
raise ValidationError("ISBN 欄位不能為空。")
if not self.is_valid_isbn(self.isbn):
raise ValidationError("輸入的 ISBN 無效,請檢查後再試。")
return True
#book_views.xml
<odoo>
<record id="view_book_form" model="ir.ui.view">
<field name="name">library.books.form</field>
<field name="model">library.books</field>
<field name="arch" type="xml">
<form string="Book">
<sheet>
<group>
<field name="name"/>
<field name="isbn"/>
<field name="book_type"/>
<field name="description"/>
<field name="stock"/>
<field name="price_twd"/>
<field name="average_rating"/>
<field name="publish_date"/>
<field name="last_rent_date"/>
<field name="rentable"/>
<field name="cover_image" widget="image"/>
</group>
<footer>
<button name="check_isbn" string="檢查 ISBN" type="object" class="btn-primary"/>
</footer>
</sheet>
</form>
</field>
</record>
<record id="view_book_tree" model="ir.ui.view">
<field name="name">library.books.tree</field>
<field name="model">library.books</field>
<field name="arch" type="xml">
<tree string="Books">
<field name="name"/>
<field name="isbn"/>
<field name="book_type"/>
<field name="stock"/>
<field name="price_twd"/>
<field name="average_rating"/>
<field name="publish_date"/>
<field name="last_rent_date"/>
<field name="rentable"/>
</tree>
</field>
</record>
<!-- 新增 search view -->
<record id="view_book_search" model="ir.ui.view">
<field name="name">library.books.search</field>
<field name="model">library.books</field>
<field name="arch" type="xml">
<search string="Search Books">
<field name="name" string="Book Name"/>
<field name="isbn" string="ISBN"/>
<field name="book_type" string="Book Type"/>
<field name="stock" string="Stock"/>
<filter name="filter_rentable" string="Rentable" domain="[('rentable', '=', True)]"/>
<filter name="filter_not_rentable" string="Not Rentable" domain="[('rentable', '=', False)]"/>
<group expand="0" string="Group By">
<filter string="Book Type" group_by="book_type"/>
<filter string="Rentable" group_by="rentable"/>
</group>
</search>
</field>
</record>
</odoo>
"""
<group expand="0" string="Group By">
<filter string="Book Type" group_by="book_type"/>
<filter string="Rentable" group_by="rentable"/>
</group>
有問題,請用 Odoo17 語法修正
Odoo 17 引入了一些新的語法變更,特別是在檢視文件(views)中,以下是需要修正的部分:
Group By Filters: 在 Odoo 17 中,<group> 和 <filter> 元素已經有了一些改變。group_by 參數應該直接放在 <filter> 元素內,並且需要確保用法符合最新語法。
<footer> 位置: 為了更好地控制按鈕,建議將 <footer> 放在 <form> 標籤的外部結束處。
以下是修正後的代碼,符合 Odoo 17 的語法:
<odoo>
<record id="view_book_form" model="ir.ui.view">
<field name="name">library.books.form</field>
<field name="model">library.books</field>
<field name="arch" type="xml">
<form string="Book">
<sheet>
<group>
<field name="name"/>
<field name="isbn"/>
<field name="book_type"/>
<field name="description"/>
<field name="stock"/>
<field name="price_twd"/>
<field name="average_rating"/>
<field name="publish_date"/>
<field name="last_rent_date"/>
<field name="rentable"/>
<field name="cover_image" widget="image"/>
</group>
</sheet>
<footer>
<button name="check_isbn" string="檢查 ISBN" type="object" class="btn-primary"/>
</footer>
</form>
</field>
</record>
<record id="view_book_tree" model="ir.ui.view">
<field name="name">library.books.tree</field>
<field name="model">library.books</field>
<field name="arch" type="xml">
<tree string="Books">
<field name="name"/>
<field name="isbn"/>
<field name="book_type"/>
<field name="stock"/>
<field name="price_twd"/>
<field name="average_rating"/>
<field name="publish_date"/>
<field name="last_rent_date"/>
<field name="rentable"/>
</tree>
</field>
</record>
<!-- 新增 search view -->
<record id="view_book_search" model="ir.ui.view">
<field name="name">library.books.search</field>
<field name="model">library.books</field>
<field name="arch" type="xml">
<search string="Search Books">
<field name="name" string="Book Name"/>
<field name="isbn" string="ISBN"/>
<field name="book_type" string="Book Type"/>
<field name="stock" string="Stock"/>
<filter name="filter_rentable" string="Rentable" domain="[('rentable', '=', True)]"/>
<filter name="filter_not_rentable" string="Not Rentable" domain="[('rentable', '=', False)]"/>
<!-- 修正 group by 的語法 -->
<group>
<filter string="Book Type" context="{'group_by':'book_type'}"/>
<filter string="Rentable" context="{'group_by':'rentable'}"/>
</group>
</search>
</field>
</record>
</odoo>
主要修改點:
Group By 語法修正: 在 和 元素中,已經更新語法為使用 context="{'group_by':'field_name'}"。
<footer>
<button name="check_isbn" string="檢查 ISBN" type="object" class="btn-primary"/>
</footer>
請將上面這段的 check_isbn button,從 footer 改成 header
要將 check_isbn 按鈕從 移動到 ,可以直接將按鈕標籤放在表單的 元素內,這樣按鈕會顯示在頁面的頂部而非底部。
以下是修改後的代碼:
<odoo>
<record id="view_book_form" model="ir.ui.view">
<field name="name">library.books.form</field>
<field name="model">library.books</field>
<field name="arch" type="xml">
<form string="Book">
<header>
<button name="check_isbn" string="檢查 ISBN" type="object" class="btn-primary"/>
</header>
<sheet>
<group>
<field name="name"/>
<field name="isbn"/>
<field name="book_type"/>
<field name="description"/>
<field name="stock"/>
<field name="price_twd"/>
<field name="average_rating"/>
<field name="publish_date"/>
<field name="last_rent_date"/>
<field name="rentable"/>
<field name="cover_image" widget="image"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="view_book_tree" model="ir.ui.view">
<field name="name">library.books.tree</field>
<field name="model">library.books</field>
<field name="arch" type="xml">
<tree string="Books">
<field name="name"/>
<field name="isbn"/>
<field name="book_type"/>
<field name="stock"/>
<field name="price_twd"/>
<field name="average_rating"/>
<field name="publish_date"/>
<field name="last_rent_date"/>
<field name="rentable"/>
</tree>
</field>
</record>
<!-- 新增 search view -->
<record id="view_book_search" model="ir.ui.view">
<field name="name">library.books.search</field>
<field name="model">library.books</field>
<field name="arch" type="xml">
<search string="Search Books">
<field name="name" string="Book Name"/>
<field name="isbn" string="ISBN"/>
<field name="book_type" string="Book Type"/>
<field name="stock" string="Stock"/>
<filter name="filter_rentable" string="Rentable" domain="[('rentable', '=', True)]"/>
<filter name="filter_not_rentable" string="Not Rentable" domain="[('rentable', '=', False)]"/>
<!-- 修正 group by 的語法 -->
<group>
<filter string="Book Type" context="{'group_by':'book_type'}"/>
<filter string="Rentable" context="{'group_by':'rentable'}"/>
</group>
</search>
</field>
</record>
</odoo>
修改後的部分:
<header>: 按鈕已移動到 <header> 元素中,這樣會顯示在表單頁面的頂部。